package com.example.student.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.student.entity.SysUser;
import com.example.student.entity.SysMenu;
import com.example.student.exception.BusinessException;
import com.example.student.mapper.SysUserMapper;
import com.example.student.mapper.SysMenuMapper;
import com.example.student.mapper.SysLoginLogMapper;
import com.example.student.model.LoginRequest;
import com.example.student.model.LoginResponse;
import com.example.student.entity.SysLoginLog;
import com.example.student.service.AuthService;
import com.example.student.service.TokenService;
import com.example.student.service.impl.JwtTokenServiceImpl;
import eu.bitwalker.useragentutils.UserAgent;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 认证服务实现
 */
@Service
public class AuthServiceImpl implements AuthService {

    @Resource
    private SysUserMapper userMapper;
    
    @Resource
    private SysMenuMapper menuMapper;
    
    @Resource
    private PasswordEncoder passwordEncoder;
    
    @Resource
    private JwtTokenServiceImpl tokenService;
    
    @Resource
    private AuthenticationManager authenticationManager;
    
    @Resource
    private SysLoginLogMapper loginLogMapper;

    @Override
    public LoginResponse login(LoginRequest loginRequest, HttpServletRequest request) {
        try {
            // 检查用户是否存在
            SysUser userCheck = userMapper.selectOne(
                new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getUsername, loginRequest.getUsername())
            );
            
            if (userCheck == null) {
                // 记录登录失败日志
                recordLoginLog(createFailedLogUser(loginRequest.getUsername()), request, false, "用户不存在");
                throw new BusinessException("用户不存在");
            }
            
            if (userCheck.getStatus() != 1) {
                // 记录登录失败日志
                recordLoginLog(userCheck, request, false, "账号已被禁用");
                throw new BusinessException("账号已被禁用");
            }
            
            // 认证用户
            Authentication authentication;
            try {
                authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                    )
                );
            } catch (BadCredentialsException e) {
                // 密码错误
                recordLoginLog(userCheck, request, false, "密码错误");
                throw new BusinessException("密码错误");
            } catch (DisabledException e) {
                // 账号被禁用
                recordLoginLog(userCheck, request, false, "账号已被禁用");
                throw new BusinessException("账号已被禁用");
            } catch (LockedException e) {
                // 账号被锁定
                recordLoginLog(userCheck, request, false, "账号已被锁定");
                throw new BusinessException("账号已被锁定");
            } catch (AuthenticationException e) {
                // 其他认证错误
                String errorMsg = extractErrorMessage(e);
                recordLoginLog(userCheck, request, false, "认证失败: " + errorMsg);
                throw new BusinessException("认证失败: " + errorMsg);
            }
            
            // 存储认证信息
            SecurityContextHolder.getContext().setAuthentication(authentication);
            
            // 获取认证成功的用户信息
            SysUser user = (SysUser) authentication.getPrincipal();
            
            // 记录登录日志
            recordLoginLog(user, request, true, "登录成功");
            
            // 更新用户登录时间和IP
            updateUserLoginInfo(user, request);
            
            // 生成token，tokenService会自动将token存入Redis
            String token = tokenService.generateToken(user);
            String refreshToken = tokenService.generateRefreshToken(user);
            
            // 获取用户权限
            List<String> permissions = getUserPermissions(user.getId());
            
            // 构造登录响应
            LoginResponse response = new LoginResponse();
            response.setToken(token);
            response.setRefreshToken(refreshToken);
            
            // 清空密码等敏感信息
            user.setPassword(null);
            response.setUser(user);
            
            response.setPermissions(permissions);
            
            return response;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            // 记录未预期的异常
            String errorMsg = extractErrorMessage(e);
            recordLoginLog(createFailedLogUser(loginRequest.getUsername()), request, false, "登录异常: " + errorMsg);
            throw new BusinessException("登录失败: " + errorMsg);
        }
    }
    
    /**
     * 提取异常中的关键错误信息
     */
    private String extractErrorMessage(Exception e) {
        String fullMessage = e.getMessage();
        if (fullMessage == null) {
            return "未知错误";
        }
        
        // 如果是JWT相关异常，提取关键信息
        if (fullMessage.contains("JWT") || fullMessage.contains("token")) {
            if (fullMessage.contains("signing key")) {
                return "令牌签名密钥配置错误";
            } else if (fullMessage.contains("expired")) {
                return "令牌已过期";
            } else if (fullMessage.contains("malformed")) {
                return "令牌格式错误";
            } else if (fullMessage.contains("signature")) {
                return "令牌签名验证失败";
            }
        }
        
        // 限制错误消息长度
        if (fullMessage.length() > 100) {
            return fullMessage.substring(0, 97) + "...";
        }
        
        return fullMessage;
    }
    
    /**
     * 创建登录失败用户对象（用于记录日志）
     */
    private SysUser createFailedLogUser(String username) {
        SysUser user = new SysUser();
        user.setId(0L); // 使用0表示未知用户ID
        user.setUsername(username);
        return user;
    }
    
    @Override
    public Long getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.getPrincipal() instanceof SysUser) {
            SysUser user = (SysUser) authentication.getPrincipal();
            return user.getId();
        }
        return null;
    }
    
    /**
     * 获取用户权限列表
     */
    private List<String> getUserPermissions(Long userId) {
        return menuMapper.selectPermissionsByUserId(userId)
                .stream()
                .filter(permission -> permission != null && !permission.isEmpty())
                .collect(Collectors.toList());
    }
    
    /**
     * 记录登录日志
     */
    private void recordLoginLog(SysUser user, HttpServletRequest request, boolean status, String msg) {
        SysLoginLog loginLog = new SysLoginLog();
        loginLog.setUserId(user.getId());
        loginLog.setUsername(user.getUsername());
        loginLog.setIp(getIpAddress(request));
        loginLog.setLocation(getLocationByIp(loginLog.getIp()));
        
        // 获取浏览器和操作系统信息
        String userAgentStr = request.getHeader("User-Agent");
        if (userAgentStr != null) {
            UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr);
            loginLog.setBrowser(userAgent.getBrowser() != null ? userAgent.getBrowser().getName() : "Unknown");
            loginLog.setOs(userAgent.getOperatingSystem() != null ? userAgent.getOperatingSystem().getName() : "Unknown");
        } else {
            loginLog.setBrowser("Unknown");
            loginLog.setOs("Unknown");
        }
        
        loginLog.setStatus(status ? 1 : 0);
        
        // 限制消息长度，避免超出数据库字段限制
        if (msg != null && msg.length() > 250) {
            // 如果消息过长，截取前247个字符并添加省略号
            loginLog.setMsg(msg.substring(0, 247) + "...");
        } else {
            loginLog.setMsg(msg);
        }
        
        loginLog.setLoginTime(new Date());
        
        loginLogMapper.insert(loginLog);
    }
    
    /**
     * 更新用户登录信息
     */
    private void updateUserLoginInfo(SysUser user, HttpServletRequest request) {
        user.setLastLoginTime(new Date());
        user.setLoginIp(getIpAddress(request));
        userMapper.updateById(user);
    }
    
    /**
     * 获取IP地址
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 多个代理的情况，取第一个IP地址
        if (ip != null && ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }
        return ip;
    }
    
    /**
     * 根据IP获取地理位置（简化实现）
     */
    private String getLocationByIp(String ip) {
        // 此处可以调用第三方API或者本地IP库获取地理位置
        // 简化实现，仅返回IP
        return "IP: " + ip;
    }
} 